package com.happycat.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.happycat.base.R;
import com.happycat.base.context.BaseContextHandler;
import com.happycat.base.enums.UserLoginType;
import com.happycat.base.exception.RestApiException;
import com.happycat.config.Audience;
import com.happycat.config.WxAppProperties;
import com.happycat.config.WxPayBean;
import com.happycat.mapper.SocialTransMapper;
import com.happycat.mapper.UserAccountMapper;
import com.happycat.mobile.model.UserAccount;
import com.happycat.mobile.service.UserAccountService;
import com.happycat.social.dto.SocialTransDto;
import com.happycat.social.entity.SocialTrans;
import com.happycat.social.entity.SocialVoiceRoom;
import com.happycat.social.service.ISocialVoiceRoomService;
import com.happycat.social.service.ISocialWxService;
import com.happycat.social.wx.PayStatusVo;
import com.happycat.social.wx.WxPayOrderNotifyResult;
import com.happycat.utils.JwtTokenUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Author shenbin
 * @CreatedDate 2020-09-14
 * @Modified By:
 */
@Slf4j
@Service
public class SocialWxServiceImpl implements ISocialWxService {
    private OkHttpClient okHttpClient = new OkHttpClient();
    private String notifyUrl;
    private String refundNotifyUrl;
    private static final String USER_PAYING = "USERPAYING";

    public WxPayApiConfig getApiConfig() {
        WxPayApiConfig apiConfig;

        try {
            apiConfig = WxPayApiConfigKit.getApiConfig(wxPayBean.getAppId());
        } catch (Exception e) {
            apiConfig = WxPayApiConfig.builder()
                    .appId(wxPayBean.getAppId())
                    .mchId(wxPayBean.getMchId())
                    .partnerKey(wxPayBean.getPartnerKey())
                    .certPath(wxPayBean.getCertPath())
                    .domain(wxPayBean.getDomain())
                    .build();
        }
        notifyUrl = apiConfig.getDomain().concat("/social/pay/payNotify");
//        refundNotifyUrl = apiConfig.getDomain().concat("/social/pay/refundNotify");
        return apiConfig;
    }

    @PostConstruct
    public void initWxPayConfig(){
        //初始化支付环境
        WxPayApiConfigKit.setThreadLocalWxPayApiConfig(getApiConfig());
    }


    @Autowired
    WxPayBean wxPayBean;

    @Autowired
    private WxAppProperties wxAppProperties;

    @Autowired
    private UserAccountMapper userAccountMapper;
    @Autowired
    private Audience audience;

    @Autowired
    private SocialTransMapper socialTransMapper;
    @Autowired
    private ISocialVoiceRoomService socialVoiceRoomService;

    @Autowired
    private UserAccountService userAccountService;

    @Override
    public UserAccount wxLogin(int loginType, String raw) {
        try {
            if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
                return wechatSafeLogin(UserLoginType.MP_WEIXIN.getCode(), raw);
            }
//            else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
//                return wechatH5Login(raw);
//            }
            else if (UserLoginType.APP_WEIXIN.getCode() == loginType) {
                return wechatAppLogin(raw);
            }
            else {
                throw new RestApiException(R.fail("不支持的第三方登录平台"));
            }
        } catch (RestApiException e) {
            log.error(e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("[用户第三方登录] 异常", e);
            throw new RestApiException(R.fail("第三方登录失败"));
        }
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public String wxPay(SocialTransDto socialTransDto) {
        SocialTrans trans = new SocialTrans();
        //新增充值流水
        if(StrUtil.isNotBlank(socialTransDto.getRoomId())){
            //查询房间
            SocialVoiceRoom room = socialVoiceRoomService.checkAndGetByRoomId(socialTransDto.getRoomId());
            trans.setRoomId(room.getRoomId());
            trans.setRoomName(room.getName());
        }
        if(BaseContextHandler.getUserId()==null){
            throw new RestApiException(R.fail("用户未登录"));
        }
        trans.setTransNo(System.currentTimeMillis() + RandomUtil.randomNumbers(5));//流水号
        trans.setTransType("1");
        trans.setPayUserId(BaseContextHandler.getUserId());
        trans.setPayAccountId(BaseContextHandler.getUserId());
        trans.setReceiveUserId(BaseContextHandler.getUserId());
        trans.setReceiveAccountId(BaseContextHandler.getUserId());
        trans.setPayAmount(NumberUtil.toBigDecimal(socialTransDto.getPayAmount()));
        trans.setPayAmountType("1");//人名币
        trans.setReceiveAmount(NumberUtil.toBigDecimal(socialTransDto.getReceiveAmount()));
        trans.setReceiveAmountType("0");//猫粮
        trans.setStatus(SocialTrans.STATUS_UNPAID);
        trans.setPayChannel(socialTransDto.getPayChannel());//微信
        trans.setPaySource(socialTransDto.getPaySource());
        socialTransMapper.insert(trans);

        //发起预支付
        String rsStr = appPay(socialTransDto.getIp(), "充值", "奇乐猫充值",
                NumberUtil.mul(trans.getPayAmount(), 100).toPlainString());

        trans.setStatus(SocialTrans.STATUS_PAYING);

        socialTransMapper.updateById(trans);

        return rsStr;
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public boolean wxPayNotify(WxPayOrderNotifyResult result) {
        SocialTrans trans = socialTransMapper.selectOne(Wrappers.<SocialTrans>lambdaQuery()
                .eq(SocialTrans::getTransNo, result.getOut_trade_no()));
        if(trans==null){
            throw new RestApiException(R.fail("未找到对应支付流水"));
        }
        //校验状态
        if(trans.getStatus().intValue() == SocialTrans.STATUS_FAIL
                || trans.getStatus().intValue() == SocialTrans.STATUS_PAID){
            log.warn("状态已更新");
            return true;
        }

        //校验金额
        if(result.getTotal_fee().intValue() != NumberUtil.mul(trans.getPayAmount(), 100).intValue()){
            log.error("金额不匹配");
            throw new RestApiException(R.fail("金额不匹配"));
        }

        //更新流水状态
        if(WxPayKit.codeIsOk(result.getResult_code())){
            trans.setStatus(SocialTrans.STATUS_PAID);
            trans.setThirdTransNo(result.getTransaction_id());

            //新增猫粮
            //新增账户变动流水, 修改余额, 增加经验值
            userAccountService.updateBalance(trans.getReceiveAccountId(), trans.getReceiveAmount().doubleValue(),
                    1, trans.getId()+"", 1, trans.getReceiveAmount().intValue());
        }else
            trans.setStatus(SocialTrans.STATUS_FAIL);

        //更新流水
        socialTransMapper.updateById(trans);

        //校验是否首充, 用户充值成功时触发校验，并做对应的事件处理
        int count = socialTransMapper.selectCount(Wrappers.<SocialTrans>lambdaQuery()
                .eq(SocialTrans::getPayAccountId, trans.getPayAccountId())
                .eq(SocialTrans::getStatus, SocialTrans.STATUS_PAID));
        if(count == 0){
            //TODO 赠送首充礼品等
        }
        return true;
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public SocialTrans wxPayStatus(Long transId, String ip) {
        //查询流水
        SocialTrans trans = socialTransMapper.selectById(transId);
        if(trans == null){
            throw new RestApiException(R.fail("未找到充值流水"));
        }
        if(trans.getStatus().intValue() == SocialTrans.STATUS_FAIL
                || trans.getStatus().intValue() == SocialTrans.STATUS_PAID){
            return trans;
        }
        if(trans.getStatus().intValue() == SocialTrans.STATUS_PAYING){
            //查询微信支付状态
            PayStatusVo vo = wx_payStatus(trans.getTransNo(), trans.getThirdTransNo());
            if(WxPayKit.codeIsOk(vo.getTradeState())){
                trans.setStatus(SocialTrans.STATUS_PAID);
                trans.setThirdTransNo(vo.getThirdTransNo());
            }else if(vo.getTradeState().equals("USERPAYING")){
                return trans;
            }else if(vo.getTradeState().equals("NOTPAY")){//未支付
                trans.setStatus(SocialTrans.STATUS_UNPAID);
            }else
                trans.setStatus(SocialTrans.STATUS_FAIL);
            socialTransMapper.updateById(trans);
            return trans;
        }else
            return trans;
    }

    /** 微信支付结果查询 */
    private PayStatusVo wx_payStatus(String busiOrderNo, String wxOrderNo){
        return wx_payStatus(null, busiOrderNo, wxOrderNo);
    }

    /** 微信支付结果查询 */
    private PayStatusVo wx_payStatus(String ip, String busiOrderNo, String wxOrderNo){
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = OrderQueryModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .out_trade_no(busiOrderNo)
                .transaction_id(wxOrderNo)
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.orderQuery(false, params);

        log.info(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            throw new RestApiException(R.fail(returnMsg));
        }
        String resultCode = result.get("result_code");
        if (WxPayKit.codeIsOk(resultCode)) {
            String trade_state = result.get("trade_state");
            String transaction_id = result.get("transaction_id");
            String trade_state_desc = result.get("trade_state_desc");
            PayStatusVo vo = new PayStatusVo();
            vo.setThirdTransNo(transaction_id);
            vo.setTradeState(trade_state);
            vo.setTradeStateDesc(trade_state_desc);
            return vo;
        }else{
            String err_code_des = result.get("err_code_des");
            throw new RestApiException(R.fail(err_code_des));
        }
    }

    /** 微信app支付 */
    private String appPay(String ip, String body, String attach, String amount){
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(body)
                .attach(attach)
                .out_trade_no(WxPayKit.generateStr())
                .total_fee(amount)
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.APP.getTradeType())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);

        log.info(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            throw new RestApiException(R.fail(returnMsg));
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            throw new RestApiException(R.fail(returnMsg));
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");

        Map<String, String> packageParams = WxPayKit.appPrepayIdCreateSign(wxPayApiConfig.getAppId(), wxPayApiConfig.getMchId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String jsonStr = JSON.toJSONString(packageParams);
        log.info("返回apk的参数:" + jsonStr);
        return jsonStr;
    }

    /*************************第三方登录 START******************************/

    /**
     * 微信小程序第三方登录，或微信APP安全登录
     * @param loginType MP_WEIXIN(1, "WX小程序登录"), APP_WEIXIN(2, "WX第三方登录")
     * @param raw
     * @return
     * @throws Exception
     */
    private UserAccount wechatSafeLogin(Integer loginType, String raw) throws IOException {
        //微信第三方登录
        JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
        String code = thirdPartJsonObject.getString("code");
        String body = okHttpClient.newCall(new Request.Builder()
                .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxAppProperties.getMiniAppId() : wxAppProperties.getAppId()) +
                        "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxAppProperties.getMiniAppSecret() : wxAppProperties.getAppSecret()) +
                        "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
        JSONObject jsonObject = JSONObject.parseObject(body);
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String unionid = jsonObject.containsKey("jsonObject")? jsonObject.getString("unionid") : null;
            String miniOpenId = jsonObject.getString("openid");
            UserAccount ws = new UserAccount();
            ws.setWxOpenid(miniOpenId);
            ws.setThirdLoginType(loginType+"");
            List<UserAccount> userDOS = userAccountMapper.selectList(new QueryWrapper<>(ws));
            UserAccount userDO;
            if (CollectionUtils.isEmpty(userDOS)) {
                //若用户为空，则注册此用户
//                Date now = new Date();
                UserAccount newUserDO = new UserAccount();
                newUserDO.setThirdLoginType(loginType+"");
                newUserDO.setWxOpenid(miniOpenId);
                newUserDO.setUnionId(unionid);
//                newUserDO.setLastLoginIp(ip);
//                newUserDO.setGmtLastLogin(now);
//                newUserDO.setGmtUpdate(now);
//                newUserDO.setGmtCreate(now);
                newUserDO.setAccountName("wx_"+miniOpenId);
                userAccountMapper.insert(newUserDO);
                //这一步是为了封装上数据库上配置的默认值
                userDO = userAccountMapper.selectById(newUserDO.getId());

                //TODO 获取微信头像等信息
            }
            else {
                //更新时间等
                userDO = userDOS.get(0);
//                UserAccount userUpdateDO = new UserAccount();
//                userUpdateDO.setId(userDO.getId());
//                userUpdateDO.setGmtLastLogin(new Date());
//                userUpdateDO.setLastLoginIp(ip);
//                userMapper.updateById(userUpdateDO);
            }
            //检查帐号是否已经冻结
            if (userDO.getStatus().intValue() != 1) {
                throw new RestApiException(R.fail("用户不可用"));
            }
            //设置token
            userDO.setToken(JwtTokenUtil.createJWT(userDO, audience));
            return userDO;
        } else {
            log.info("[微信登录] 回复失败 回复报文：" + body);
            throw new RestApiException(R.fail("第三方登录失败"));
        }
    }

    /**
     * 微信公众号浏览器H5登录
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
//    private UserDTO wechatH5Login(String ip, String raw) throws Exception {
//        //H5 微信公众号网页登录
//        String json = okHttpClient.newCall(
//                new Request.Builder().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="
//                        + wxAppProperties.getH5AppId() + "&secret=" + wxAppProperties.getH5AppSecret() + "&code=" + raw + "&grant_type=authorization_code").build()).execute().body().string();
//        JSONObject jsonObject = JSONObject.parseObject(json);
//        Integer errcode = jsonObject.getInteger("errcode");
//        if (errcode == null || errcode == 0) {
//            String openid = jsonObject.getString("openid");
//            List<UserDO> userDOS = userMapper.selectList(new QueryWrapper<UserDO>().eq("open_id", openid).eq("login_type", UserLoginType.H5_WEIXIN.getCode()));
//            if (!CollectionUtils.isEmpty(userDOS)) {
//                //若用户已经注册，则直接返回用户
//                String accessToken = GeneratorUtil.genSessionId();
//                UserDTO userDTO = new UserDTO();
//                BeanUtils.copyProperties(userDOS.get(0), userDTO);
//                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
//                userDTO.setAccessToken(accessToken);
//                return userDTO;
//            } else {
//                String userAccessToken = jsonObject.getString("access_token");
//                //通过用户AccessToken换取用户信息
//                String userInfoJson = okHttpClient.newCall(
//                        new Request.Builder().url("https://api.weixin.qq.com/sns/userinfo?access_token="
//                                + userAccessToken + "&openid=" + openid + "&lang=zh_CN").build()).execute().body().string();
//                JSONObject userInfoJsonObject = JSONObject.parseObject(userInfoJson);
//                Date now = new Date();
//                UserDO newUserDO = new UserDO();
//                newUserDO.setLoginType(UserLoginType.H5_WEIXIN.getCode());
//                newUserDO.setNickname(userInfoJsonObject.getString("nickname"));
//                newUserDO.setAvatarUrl(userInfoJsonObject.getString("headimgurl"));
//                newUserDO.setGender(userInfoJsonObject.getInteger("sex"));
//                newUserDO.setOpenId(openid);
//                newUserDO.setLastLoginIp(ip);
//                newUserDO.setGmtLastLogin(now);
//                newUserDO.setGmtUpdate(now);
//                newUserDO.setGmtCreate(now);
//                userMapper.insert(newUserDO);
//                //这一步是为了封装上数据库上配置的默认值
//                UserDO userDO = userMapper.selectById(newUserDO.getId());
//                String accessToken = GeneratorUtil.genSessionId();
//                UserDTO userDTO = new UserDTO();
//                BeanUtils.copyProperties(userDO, userDTO);
//                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
//                userDTO.setAccessToken(accessToken);
//                return userDTO;
//            }
//        } else {
//            throw new RestApiException(R.fail("第三方登录失败"));
//        }
//    }
//
    /**
     * 微信APP登录
     * @param raw
     * @return
     * @throws Exception
     */
    private UserAccount wechatAppLogin(String raw) throws Exception {
        //UNI-APP 的 微信APP登录 APPSecret是保存在前端的。这点非常不安全。但是用了他的框架，也没有办法
        JSONObject jsonObject = JSONObject.parseObject(raw);
        JSONObject authResult = jsonObject.getJSONObject("authResult");
        String openid = authResult.getString("openid");
        String unionid = jsonObject.containsKey("jsonObject")? jsonObject.getString("unionid") : null;
        List<UserAccount> userDOS = userAccountMapper.selectList(new QueryWrapper<UserAccount>().eq("open_id", openid).eq("login_type", UserLoginType.APP_WEIXIN.getCode()));
        UserAccount userDO;
        if (CollectionUtils.isEmpty(userDOS)) {
            //创建新用户
            Date now = new Date();
            UserAccount newUserDO = new UserAccount();
//            newUserDO.setLoginType(UserLoginType.APP_WEIXIN.getCode());
            newUserDO.setThirdLoginType(UserLoginType.APP_WEIXIN.getCode()+"");
            newUserDO.setWxOpenid(openid);
            newUserDO.setUnionId(unionid);
//            newUserDO.setLastLoginIp(ip);
//            newUserDO.setGmtLastLogin(now);
//            newUserDO.setGmtUpdate(now);
//            newUserDO.setGmtCreate(now);
            newUserDO.setAccountName("wx_"+openid);
            userAccountMapper.insert(newUserDO);
            //这一步是为了封装上数据库上配置的默认值
            userDO = userAccountMapper.selectById(newUserDO.getId());
            //TODO 查询用户头像信息等
        } else {
            userDO = userDOS.get(0);
//            UserDO userUpdateDO = new UserDO();
//            userUpdateDO.setId(userDO.getId());
//            userUpdateDO.setGmtLastLogin(new Date());
//            userUpdateDO.setLastLoginIp(ip);
//            userMapper.updateById(userUpdateDO);
        }
        //检查帐号是否已经冻结
        //检查帐号是否已经冻结
        if (userDO.getStatus().intValue() != 1) {
            throw new RestApiException(R.fail("用户不可用"));
        }
        //设置token
        userDO.setToken(JwtTokenUtil.createJWT(userDO, audience));
        return userDO;
    }

    /*************************第三方登录 END******************************/
}
